package com.young.common.util;


import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * RSA算法加密工具类,适用于1024位证书
 * 支持公私钥加解密(支持分段),生成公私钥
 * @author wangjiyu@imdada.cn
 * @create 2018/9/7
 */
public class RSAUtil {

    public static String KEY_ALGORITHM = "RSA";//RSA算法
    public static String SIGN_ALGORITHMS = "SHA1WithRSA";//签名算法

    //使用1024位的证书,加密分段为117,解密分段为128
    //若使用2048位的证书,加密分段为245,解密分段为256
    public static int SIGMENT_SIZE_EN = 117;//分段大小
    public static int SIGMENT_SIZE_DE = 128;//解密时分段大小

    /**
     * 私钥加密
     * @param bytes 待加密数据
     * @param privateKeyStr 私钥
     * @return 加密后的数据
     * @throws Exception
     */
    public static byte[] encryptByPrivate(byte[] bytes, String privateKeyStr) throws Exception{
        try {
            //私钥文本
            byte[] privateBytes = Base64.getDecoder().decode(privateKeyStr);//base64将私钥文本解析为byte数组
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateBytes);//私钥声明，PKCS8规范
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密钥工厂，根据算法生成密钥工厂
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);//生成私钥对象
            //数据加密
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);//加密模式
            return segmentHandle(bytes, cipher, SIGMENT_SIZE_EN);
        } catch (Exception e){
            throw new Exception(e);
        }
    }

    /**
     * 私钥解密
     * @param bytes 待解密数据
     * @param privateKeyStr 私钥
     * @return 解密后的数据
     * @throws Exception
     */
    public static byte[] decryptByPrivate(byte[] bytes, String privateKeyStr) throws Exception{
        try {
            //私钥文本
            byte[] privateBytes = Base64.getDecoder().decode(privateKeyStr);//base64将私钥文本解析为byte数组
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateBytes);//私钥声明，PKCS8规范
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密钥工厂，根据算法生成密钥工厂
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);//生成私钥对象
            //数据解密
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);//解密模式
            return segmentHandle(bytes, cipher, SIGMENT_SIZE_DE);
        } catch (Exception e){

            throw new Exception(e);
        }
    }

    /**
     * 公钥加密
     * @param bytes 待加密数据
     * @param publicKeyStr 公钥
     * @return 加密后的数据
     * @throws Exception
     */
    public static byte[] encryptByPublic(byte[] bytes, String publicKeyStr) throws Exception{
        try {
            //私钥文本
            byte[] privateBytes = Base64.getDecoder().decode(publicKeyStr);//base64将公钥文本解析为byte数组
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(privateBytes);//公钥声明，X509规范
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密钥工厂，根据算法生成密钥工厂
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);//生成公钥对象
            //数据加密
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);//加密模式
            return segmentHandle(bytes, cipher, SIGMENT_SIZE_EN);
        } catch (Exception e){
            throw new Exception(e);
        }
    }

    /**
     * 公钥解密
     * @param bytes 待解密数据
     * @param publicKeyStr 公钥
     * @return 解密后的数据
     * @throws Exception
     */
    public static byte[] decryptByPublic(byte[] bytes, String publicKeyStr) throws Exception{
        try {
            //私钥文本
            byte[] privateBytes = Base64.getDecoder().decode(publicKeyStr);//base64将公钥文本解析为byte数组
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(privateBytes);//公钥声明，X509规范
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);//密钥工厂，根据算法生成密钥工厂
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);//生成公钥对象
            //数据解密
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, publicKey);//解密模式
            return segmentHandle(bytes, cipher, SIGMENT_SIZE_DE);
        } catch (Exception e){
            throw new Exception(e);
        }
    }

    /**
     * 生成密钥对
     * @return 密钥数组，长度为2，第一个为公钥，第二个为私钥
     * @throws Exception
     */
    public static String[] generateKeyPair() throws Exception{
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);//密钥对生成器
            KeyPair keyPair = keyPairGenerator.generateKeyPair();//生成密钥对
            PublicKey publicKey = keyPair.getPublic();//公钥
            PrivateKey privateKey = keyPair.getPrivate();//私钥
            return new String[]{Base64.getEncoder().encodeToString(publicKey.getEncoded()), Base64.getEncoder().encodeToString(privateKey.getEncoded())};
        } catch (Exception e){
            throw new Exception(e);
        }
    }

    /**
     * 签名
     * @param msg 内容
     * @param privateKey 私钥
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws InvalidKeyException
     * @throws UnsupportedEncodingException
     * @throws SignatureException
     */
    public static String sign(String msg, String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, UnsupportedEncodingException, SignatureException {
        PKCS8EncodedKeySpec priPKCS8  = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey) );
        KeyFactory keyf = KeyFactory.getInstance("RSA");
        PrivateKey priKey = keyf.generatePrivate(priPKCS8);
        java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
        signature.initSign(priKey);
        signature.update(msg.getBytes("UTF-8"));
        byte[] signed = signature.sign();
        return Base64.getEncoder().encodeToString(signed);
    }

    /**
     * 验签
     * @param msg 内容
     * @param signStr 签名
     * @param publicKey 公钥
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws InvalidKeyException
     * @throws UnsupportedEncodingException
     * @throws SignatureException
     */
    public static boolean verify(String msg, String signStr, String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, UnsupportedEncodingException, SignatureException {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] encodedKey = Base64.getDecoder().decode(publicKey);
        PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
        java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
        signature.initVerify(pubKey);
        signature.update(msg.getBytes("UTF-8"));
        return signature.verify(Base64.getDecoder().decode(signStr));
    }

    /**
     * 分段处理加解密
     * @param bytes 待加密(或解密)数据
     * @param cipher 加解密对象
     */
    private static byte[] segmentHandle(byte[] bytes, Cipher cipher, int segmentSize) throws BadPaddingException, IllegalBlockSizeException, IOException {
        //分段处理
        int len = bytes.length;//总大小
        int offset = 0;//当前处理长度
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        while (offset < len){
            int size = (len - offset) > segmentSize ? segmentSize : len - offset;
            byteArrayOutputStream.write(cipher.doFinal(bytes, offset, size));//写入流
            offset += size;//处理长度递增
        }
        byte[] res = byteArrayOutputStream.toByteArray();
        byteArrayOutputStream.close();
        return res;
    }

    public static void main(String[] args) {
        try {
            String[] pair = generateKeyPair();
            System.out.println(pair[0]);
            System.out.println(pair[1]);
            if (true)return;
            String k = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALG1MAQ05Iu68v1g89IrXdiTy+Fl6YC3mdEvJ908KaBJiSYuERQ/Kkhew2yJ9bSSXEx+7mV87RQFQWXwZwzkMPigFDhBwzHCXLfKZgcEbKTb9jZ39cn0swtVCxuRzoY/tE5NCAfoSrydII3AnxdaGmyL7MuQqH3sLTQaXr/WkgU5AgMBAAECgYEAl/Vw1tgH6vh1GngSZtDQkgdoGxjTgE7/yCHDu+nbSoY3Mh/IWQDefj4SbDjYk41oQb10y/H0pN8NmkblR9/FgxhGIY8W1zz1+nUTwbQoYwolUtpYRqXQ4AhJQg7gbXIcE2mtU0SexamkGug3LY0AGqXCKJJ/SBd/zDLPF/yNVoECQQDYgEC6l2Ez1uEU37T+xHeXLx9wM7m7CYLy8lfUzR9GnlGNAMBU19ncfqLXODu88jGZregCl26Fh5UoecpFZJOpAkEA0iEVvYU4P6K1A79hn1vdt7WDESdt4AeLa5QpIqzTxxl6wLEnN0YW/uow/BNi5sX9m06IduAJkF9Rxy5zlqTfEQJAb9szljQIIIRwnhxGoypWr2HFWG8hEqWDZooXHvcrpYy+jZ9Jv4wWH3m+IHwJQUHQppnXOR+MFqlKQzNJcjjYoQJAehT2yJFpyK8DdBXw5eE9w8oVSBYcZBh+DBacf6Thtb877k/+dMKoAZqWsx2gTGujmGPcLRSk+dgnbw2x4kgPwQJAGytEcvqC1n1Qhkn94pni7eaNjZIosTwAW1an1CXvS1dVVmxMj4nxvxGd4bwkOBmh+VDWhZjTq/v+p3ldDsqs3g==";
            String msg = "{\"requestId\":271526374847,\"code\":2,\"info\":\"成功\",\"finishTime\":\"2018-11-27 12:30:30\"}";
            String sign = sign(msg, k);
            System.out.println(sign);
            System.out.println(verify(msg, sign, "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxtTAENOSLuvL9YPPSK13Yk8vhZemAt5nRLyfdPCmgSYkmLhEUPypIXsNsifW0klxMfu5lfO0UBUFl8GcM5DD4oBQ4QcMxwly3ymYHBGyk2/Y2d/XJ9LMLVQsbkc6GP7ROTQgH6Eq8nSCNwJ8XWhpsi+zLkKh97C00Gl6/1pIFOQIDAQAB"));
            /*String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCFc1Zvumx2SydZFeo6lz/f7mNMGVEqZ5QWFaeQ34dR8WDE6BZwrtxz/WBKMS6YSO8gzF3mXwT4qc4+sBH63iKY6QQRG0YUW82FNIfOuSjmo/fo1TmnvlUw3axMFgd1u8VmTLtceK4aR9Y1WBg/XuVYmgdFW8t72FKT+C+UCr4DnQIDAQAB";
            String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAIVzVm+6bHZLJ1kV6jqXP9/uY0wZUSpnlBYVp5Dfh1HxYMToFnCu3HP9YEoxLphI7yDMXeZfBPipzj6wEfreIpjpBBEbRhRbzYU0h865KOaj9+jVOae+VTDdrEwWB3W7xWZMu1x4rhpH1jVYGD9e5ViaB0Vby3vYUpP4L5QKvgOdAgMBAAECgYBY0douxNJZrU2EEnzyACboQP7f8F3ALm42gLUlTFEBbfktNRdyaj+aB4Ll/nP3sVtO5hZ9NLLXWhVGTeO5p804JyVYmq4UqmQ4jH55Z2z/NcCnoo8yc5Uxkfdb1lr2hZp9qg5/HxKEzWxB0NJ0w5s2QTCW+MQ2emQLleKwlNTS1QJBAM4iWtuYFrHxOi9lN5vRK9Xj+2qGQNbKNW7evUMELkXkjyAg8lHiGA8f0rr2xnUpDJyqAGtvsEeADY8xSm3J+ysCQQClu8Uw6kN0dYn1/jJ8PHnoM5misqsUgGTByoNXHo2Y+w/IjakHWNeMYW4U09Pt3JC/opTNXFJri5YoNaurWPhXAkBQLVavwTlanxxloPX/Ol3VhSaeaXfA08fyxtpQvciN0PfGpGn8ZXe8MkGSDThmC8BnE0y0fS3QscMUZjSv/XcfAkEAgANaW98G/WRwjLaszjN0QjvhFr+Ez5Agw2MmMduiqaB634E0Jz2jOIZlHedEkjzE/ZCZ+UmvS+yErkhOckqFfwJBAI3x/z4uFXg1xxZBnVLGPUGl2OH1/63sZhQPvKvi9z/y6Yos/CRh6drL2GkSaEp5VsKklDwpwb9JguFT3Qf4Ei0=";
            String msg = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIzMjdkMzM5MmIyMzA0N2Q4YWE3YjJjMmNiZGE1NmQwOCIsImV4cCI6eyJkYXRlIjoxNiwiZGF5IjowLCJob3VycyI6MTUsIm1pbnV0ZXMiOjUwLCJtb250aCI6OCwic2Vjb25kcyI6MCwidGltZSI6MTUzNzA4NDIwMDIzOCwidGltZXpvbmVPZmZzZXQiOi00ODAsInllYXIiOjExOH0sImlhdCI6eyJkYXRlIjoxNiwiZGF5IjowLCJob3VycyI6MTUsIm1pbnV0ZXMiOjIwLCJtb250aCI6OCwic2Vjb25kcyI6MCwidGltZSI6MTUzNzA4MjQwMDIzOCwidGltZXpvbmVPZmZzZXQiOi00ODAsInllYXIiOjExOH0sImlzcyI6InlvdW5nIiwic3ViIjoiMzI3ZDMzOTJiMjMwNDdkOGFhN2IyYzJjYmRhNTZkMDgiLCJ1c2VySWQiOiIzMjdkMzM5MmIyMzA0N2Q4YWE3YjJjMmNiZGE1NmQwOCJ9.e9b114e35a09f5ca1053c0f1239292e74b92fe6a120e230af1b7b7eac6ebab5a";
            System.out.println("明文长度:"+msg.length()+", byte[]长度:"+msg.getBytes().length);
            byte[] b1 = encryptByPublic(msg.getBytes(), publicKey);
            System.out.println("加密后长度="+b1.length);
            System.out.println("公钥加密"+Base64.getEncoder().encodeToString(b1));
            System.out.println("私钥解密"+new String(decryptByPrivate(b1, privateKey)).equals(msg));*/
            /*String[] keys = generateKeyPair();
            String publicKeyStr = keys[0];
            String privateKeyStr = keys[1];
            System.out.println("生成密钥对。。。");
            System.out.println("公钥："+publicKeyStr);
            System.out.println("私钥："+privateKeyStr);*/
            /*System.out.println("---------");
            String msg = "我叫王吉雨，www.imroookie.cn";
            System.out.println("明文："+msg);
            System.out.println("---------");
            byte[] b1 = encryptByPrivate(msg.getBytes(), privateKeyStr);//私钥加密
            System.out.println("私钥加密：" + Base64.getEncoder().encodeToString(b1));
            byte[] b3 = decryptByPublic(b1, publicKeyStr);//公钥解密
            System.out.println("公钥解密：" + new String(b3));
            System.out.println("---------");
            byte[] b4 = encryptByPublic(msg.getBytes(), publicKeyStr);//公钥加密
            System.out.println("公钥加密：" + Base64.getEncoder().encodeToString(b4));
            byte[] b6 = decryptByPrivate(b4, privateKeyStr);//私钥解密
            System.out.println("私钥解密：" + new String(b6));*/
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}